﻿using CrankShaftAnalysisSystem.Controller;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CrankShaftAnalysisSystem.Devices
{
    public class EncoderERM2400
    {
        private static readonly EncoderERM2400 instance = new EncoderERM2400();

        /// <summary>
        /// 显式的静态构造函数用来告诉C#编译器在其内容实例化之前不要标记其类型
        /// 静态构造函数是C#的一个新特性，其实好像很少用到。 
        /// 不过当我们想初始化一些静态变量的时候就需要用到它了。 这个构造函数是属于类的，
        /// 而不是属于哪里实例的，就是说这个构造函数只会被执行一次。 
        /// 也就是在创建第一个实例或引用任何静态成员之前，由.NET自动调用
        /// </summary>
        static EncoderERM2400() { }

        private EncoderERM2400()
        {
            Device = new EncoderIncremental(0, 1400);
        }

        public static EncoderERM2400 Instance
        {
            get
            {
                return instance;
            }
        }

        public bool NearZero(double tol = 0.035)
        {
            if (!Device.RefFound)
                return false;
            var ret = Device.ReadAngleRadians(out _);
            return Math.Abs(ret) < tol;
        }

        public EncoderIncremental Device;
    }

    public class EncoderIncremental
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ch">通道编号</param>
        /// <param name="lines">编码器线数，ERM2400是1400线，RV测试台的输入编码器是16384</param>
        public EncoderIncremental(int ch, int lines)
        {
            Lines = lines;
            Index = (ushort)ch;
        }

        public MeasureSystem.State State { get; set; } = MeasureSystem.State.IDLE;
        public int Lines { get; set; }
        public ushort Index { get; set; }
        public bool Initialised { get; private set; } = false;
        public bool RefFound { get; private set; }
        public double Angular { get; private set; }

        public int MultiTurn { get {
                return MultiTurnShadow - MultiTurnOffset;
            } }
        public int MultiTurnShadow { get; private set; } = 0;
        public int MultiTurnOffset { get; private set; } = 0;
        public double TotalDegrees { get; set; }

        public bool Init()
        {
            var ret = IK220.IK220Init(Index);
            IK220.IK220ResetRef(Index);
            RefFound = false;
            Properties.Settings.Default.EncoderRefFound = false;
            Properties.Settings.Default.Save();
            return ret;
        }

        public void ClearMultiTurn()
        {
            ReadAngleRadians(out _);
            MultiTurnOffset = MultiTurnShadow;
        }

        public void ResetRef()
        {
            IK220.IK220ResetRef(Index);
        }

        public Task WaitRef()
        {
            if (State == MeasureSystem.State.BUSY)
                throw new Exception("Already Waiting Ref");
            return Task.Run(() =>
            {
                State = MeasureSystem.State.BUSY;
                double last_ang = 0.0;
                double current_ang = 0.0;
                while(true)
                {
                    ReadAngleRadians(out current_ang);
                    Console.WriteLine($"current:{current_ang}, last:{last_ang}");
                    if (Math.Abs(last_ang) > 100 && Math.Abs(current_ang) < 30)
                    {
                        RefFound = true;
                        Properties.Settings.Default.EncoderRefFound = true;
                        Properties.Settings.Default.Save();
                        break;
                    }

                    if (SafeController.ManualStop)
                    {
                        Console.WriteLine("[Safe Controller]: StopAll.");
                        PLCMaster.Instance.SetSpeed(1, 0);
                        break;
                    }
                    last_ang = current_ang;
                    Thread.Sleep(100);
                }

                State = MeasureSystem.State.FINISHED;
            });
        }

        public bool Begin()
        {
            bool ret = true;
            ret &= IK220.IK220StartRef(Index);
            //ret &= IK220.IK220DoRef(Index);
            Initialised = ret;
            RefFound = Properties.Settings.Default.EncoderRefFound;
            return ret;
        }

        public double ReadAngleRadians(out double degrees)
        {
            double PulseOutput = 0.0;
            if (!IK220.IK220Read48(Index, 0, out PulseOutput))
                throw new Exception("Read Angle Error.");
            degrees = PulseOutput / Lines * 360;
            TotalDegrees = degrees;
            MultiTurnShadow = (int)(degrees / 360);
            var rad = PulseOutput / Lines * Math.PI * 2;
            var ret = Tools.ReduceRadians(rad);
            Angular = ret / Math.PI * 180.0;
            if (Angular < 0) Angular += 360.0;
            return ret;
        }

    }
}
